package cn.xiaobai.captcha.config;

import cn.xiaobai.captcha.annotation.CustomConditionalOnProperty;
import cn.xiaobai.captcha.constant.Constant;
import cn.xiaobai.captcha.handler.captchatype.ChineseCaptchaHandlerAbstract;
import cn.xiaobai.captcha.handler.captchatype.GraphCaptchaHandlerAbstract;
import cn.xiaobai.captcha.handler.captchatype.MathCaptchaHandlerAbstract;
import cn.xiaobai.captcha.handler.dbtype.MysqlAbstractTypeHandler;
import cn.xiaobai.captcha.handler.dbtype.RedisAbstractTypeHandler;
import cn.xiaobai.captcha.server.CaptchaGeneralAdapterServer;
import cn.xiaobai.captcha.server.CaptchaServer;
import cn.xiaobai.captcha.utils.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;

import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;


/**
 * 自动验证码类型配置
 *
 * @Author yangdaji
 * @Date 2024/4/18
 * @PackageName cn.xiaobai.captcha.config
 * @ClassName AutoCaptchaTypeConfiguration
 */
@Slf4j
@CustomConditionalOnProperty(name = "captch.enable", havingVale = {"true", "on"})
@EnableConfigurationProperties(CaptchaProperties.class)
@Import({CaptchaGeneralAdapterServer.class, AutoCaptchaTypeConfiguration.DBCaptchaConfiguration.class})
public class AutoCaptchaTypeConfiguration {


    private static CaptchaProperties captchaProperties;

    public AutoCaptchaTypeConfiguration(CaptchaProperties captchaProperties) {
        AutoCaptchaTypeConfiguration.captchaProperties = captchaProperties;
    }

    // 验证码类型
    @Bean
    @CustomConditionalOnProperty(name = "captch.type", havingVale = {Constant.MATH})
    public MathCaptchaHandlerAbstract mathCaptchaHandler() {
        return new MathCaptchaHandlerAbstract(captchaProperties);
    }

    @Bean
    @CustomConditionalOnProperty(name = "captch.type", havingVale = {Constant.CHINESE})
    public ChineseCaptchaHandlerAbstract chineseCaptchaHandler() {
        return new ChineseCaptchaHandlerAbstract(captchaProperties);
    }

    @Bean
    @CustomConditionalOnProperty(name = "captch.type", havingVale = {Constant.GRAPH}, isDefault = true)
    public GraphCaptchaHandlerAbstract graphCaptchaHandler() {
        return new GraphCaptchaHandlerAbstract(captchaProperties);
    }

    @CustomConditionalOnProperty(name = "captch.cache", havingVale = {"true", "on"})
    @Import({DBCaptchaConfiguration.SQLDBCaptchaConfiguration.class, DBCaptchaConfiguration.NOSQLDBCaptchaConfiguration.class})
    public static class DBCaptchaConfiguration {


        /**
         * 支持关系型数据库
         */
        @CustomConditionalOnProperty(name = "captch.db", havingVale = {Constant.MYSQL})
        @ConditionalOnClass(name = {"com.mysql.jdbc.Driver", "com.mysql.cj.jdbc.Driver",
                "org.springframework.jdbc.core.JdbcTemplate", "org.mybatis.spring.SqlSessionTemplate",
                "org.apache.ibatis.session.SqlSessionFactory"})
        @Import({CaptchaServer.class})
        public static class SQLDBCaptchaConfiguration {

            // 支持 mysql
            @Bean
            public MysqlAbstractTypeHandler mysqlTypeHandler() {
                return new MysqlAbstractTypeHandler(captchaProperties);
            }
        }

        /**
         * 支持 非关系型数据库
         */
        @CustomConditionalOnProperty(name = "captch.db", havingVale = {Constant.REDIS}, isDefault = true)
        @ConditionalOnClass(name = {"org.springframework.data.redis.core.RedisTemplate",
                "org.springframework.data.redis.connection.RedisConnectionFactory"})
        @Import({RedisUtil.class})
        public static class NOSQLDBCaptchaConfiguration {

            // 支持 redis
            @Bean
            public RedisAbstractTypeHandler redisTypeHandler() {
                return new RedisAbstractTypeHandler(captchaProperties);
            }

//        @Bean
//        @ConditionalOnBean(RedisAbstractTypeHandler.class)
//        public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
//            RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
//            //关联连接工厂,后面会设置连接池
//            redisTemplate.setConnectionFactory(connectionFactory);
//            //设置key-value的序列化器
//            redisTemplate.setKeySerializer(new StringRedisSerializer());
//            redisTemplate.setValueSerializer(new StringRedisSerializer());
//            //设置hashkey-value的序列化
//            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
//            return redisTemplate;
//        }

            @Bean
            @ConditionalOnBean(RedisAbstractTypeHandler.class)
            public Object redisTemplate() {

                try {
                    Class<?> redisTemplateCls = Class.forName("org.springframework.data.redis.core.RedisTemplate");
                    Class<?> redisTemplateFactoryCls = Class.forName("org.springframework.data.redis.connection.RedisConnectionFactory");
                    Class<?> redisTemplateFactoryImplCls = Class.forName("org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory");
                    Class<?> strSerializerCls = Class.forName("org.springframework.data.redis.serializer.StringRedisSerializer");
                    Class<?> serializerCls = Class.forName("org.springframework.data.redis.serializer.RedisSerializer");

                    // 反射获取实例
                    Object redisTemplate = BeanUtils.instantiateClass(redisTemplateCls);
                    Object redisTemplateFactory = BeanUtils.instantiateClass(redisTemplateFactoryImplCls);
                    Object strSerializer = BeanUtils.instantiateClass(strSerializerCls);

                    // 获取方法
                    Method setConnectionFactory = redisTemplateCls.getMethod("setConnectionFactory", redisTemplateFactoryCls);

                    // 获取序列化方法
                    Method setKeySerializer = redisTemplateCls.getMethod("setKeySerializer", serializerCls);
                    Method setValueSerializer = redisTemplateCls.getMethod("setValueSerializer", serializerCls);
                    Method setHashKeySerializer = redisTemplateCls.getMethod("setHashKeySerializer", serializerCls);
                    Method setHashValueSerializer = redisTemplateCls.getMethod("setHashValueSerializer", serializerCls);

                    // 初始化属性
                    ReflectionUtils.invokeMethod(setConnectionFactory, redisTemplate, redisTemplateFactory);
                    ReflectionUtils.invokeMethod(setKeySerializer, redisTemplate, strSerializer);
                    ReflectionUtils.invokeMethod(setValueSerializer, redisTemplate, strSerializer);
                    ReflectionUtils.invokeMethod(setHashKeySerializer, redisTemplate, strSerializer);
                    ReflectionUtils.invokeMethod(setHashValueSerializer, redisTemplate, strSerializer);

                    return redisTemplate;
                } catch (ClassNotFoundException e) {
                    // 缺少依赖
                    log.error("缺少redis 相关依赖 : {}", e.getMessage());
                } catch (NoSuchMethodException n) {
                    log.error("未找到方法属性: {}", n.getMessage());
                }

                return null;
            }
        }

    }
}
